package com.igoal.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import com.igoal.dao.GoalDAO;
import com.igoal.dao.PrivacySettingDAO;
import com.igoal.dao.ProfileCompleteDAO;
import com.igoal.dao.UserDAOExt;
import com.igoal.dao.UserGoalDAO;
import com.igoal.dao.UserGoalEntryDAO;
import com.igoal.dao.UserGoalEntryDoneDAO;
import com.igoal.dao.UserGoalPhaseDAO;
import com.igoal.dao.UserGoalTaskDAO;
import com.igoal.dto.GoalDTO;
import com.igoal.dto.UserDTO;
import com.igoal.dto.UserGoalDTO;
import com.igoal.dto.UserGoalPhaseDTO;
import com.igoal.dto.UserGoalTaskDTO;
import com.igoal.entity.ActionTypeEnum;
import com.igoal.entity.ComponentEnum;
import com.igoal.entity.DataStatus;
import com.igoal.entity.Goal;
import com.igoal.entity.GoalStatusEnum;
import com.igoal.entity.PrivacyEnum;
import com.igoal.entity.PrivacySetting;
import com.igoal.entity.ProfileComplete;
import com.igoal.entity.User;
import com.igoal.entity.UserGoal;
import com.igoal.entity.UserGoalDone;
import com.igoal.entity.UserGoalEntry;
import com.igoal.entity.UserGoalPhase;
import com.igoal.entity.UserGoalPk;
import com.igoal.entity.UserGoalTask;
import com.igoal.entity.factory.EntityFactory;
import com.igoal.service.paging.PagingService;
import com.igoal.utils.CalendarUtils;
import com.igoal.utils.EntityUtils;
import com.igoal.web.Constants;

@Service("userGoalManager")
public class UserGoalManagerImpl extends BaseService<UserGoalDTO, UserGoal, UserGoalPk> implements
		UserGoalManager {
	private UserGoalDAO userGoalDAO;
	private UserDAOExt userDAO;
	private UserGoalEntryDAO userGoalEntryDAO;
	private UserGoalEntryDoneDAO userGoalEntryDoneDAO;
	private GoalDAO goalDAO;
	private ProfileCompleteDAO profileCompleteDAO;
	private UserGoalTaskDAO userGoalTaskDAO;
	private PrivacySettingDAO privacySettingDAO;
	private UserGoalPhaseDAO userGoalPhaseDAO;
	

	private ActionLoggerService actionLoggerService;
	
	@Autowired
	public void setUserGoalPhaseDAO(UserGoalPhaseDAO userGoalPhaseDAO) {
		this.userGoalPhaseDAO = userGoalPhaseDAO;
	}

	@Autowired
	public void setUserGoalDAO(UserGoalDAO userGoalDAO) {
		this.userGoalDAO = userGoalDAO;
	}
	
	@Autowired
	public void setUserDAO(UserDAOExt userDAO) {
		this.userDAO = userDAO;
	}

	@Autowired
	public void setGoalDAO(GoalDAO goalDAO) {
		this.goalDAO = goalDAO;
	}
	
	@Autowired
	public void setUserGoalEntryDAO(UserGoalEntryDAO userGoalEntryDAO) {
		this.userGoalEntryDAO = userGoalEntryDAO;
	}

	@Autowired
	public void setProfileCompleteDAO(ProfileCompleteDAO profileCompleteDAO) {
		this.profileCompleteDAO = profileCompleteDAO;
	}

	@Autowired
	public void setUserGoalEntryDoneDAO(UserGoalEntryDoneDAO userGoalEntryDoneDAO) {
		this.userGoalEntryDoneDAO = userGoalEntryDoneDAO;
	}

	@Autowired
	public void setUserGoalTaskDAO(UserGoalTaskDAO userGoalTaskDAO) {
		this.userGoalTaskDAO = userGoalTaskDAO;
	}

	@Autowired
	public void setActionLoggerService(ActionLoggerService actionLoggerService) {
		this.actionLoggerService = actionLoggerService;
	}
	
	@Autowired
	public void setPrivacySettingDAO(PrivacySettingDAO privacySettingDAO) {
		this.privacySettingDAO = privacySettingDAO;
	}

	public List<UserGoalDTO> getDoingGoal(Long userId) {
		List<UserGoalDTO> result = getUserGoalOption(userId, false);
		return result;
	}
	
	public List<UserGoalDTO> getCompletedGoal(Long userId) {
		List<UserGoalDTO> result = getUserGoalOption(userId, true);
		return result;
	}
	
	public List<UserGoalDTO> getUserGoalOption(Long userId, boolean completed) {
		List<UserGoalDTO> result = new ArrayList<UserGoalDTO>();
		GoalStatusEnum goalStatus = completed ? GoalStatusEnum.FINISHED
				: GoalStatusEnum.DOING;

		Map<String, Object> criteria = new HashMap<String, Object>();
		criteria.put("status", DataStatus.USING);
		criteria.put("pk.user.id", userId);
		criteria.put("goalStatus", goalStatus);

		List<UserGoal> userGoals = userGoalDAO.pagingDependOnOrderProperty(
				criteria, "updateDate", null, false,
				PagingService.MAX_GOALS_OF_USER);
		if (userGoals != null) {
			for (UserGoal userGoal : userGoals) {
				UserGoalDTO dto = new UserGoalDTO();
				int entryCount = countGoalEntry(userGoal.getGoal().getId(), null);
				dto.setEntryCount(entryCount);
				dto.initFrom(userGoal);
				result.add(dto);
			}
		}
		return result;
	}
	
	public int countGoalEntry(Long goalId, Long userId) {
		return userGoalEntryDAO.countEntries(userId, goalId);
	}
	
	public UserGoalDTO createNewGoal(GoalDTO goalDTO, UserDTO userDTO) {
		if (goalDTO == null || userDTO == null
				|| StringUtils.isBlank(goalDTO.getTitle())
				|| !allowCreateNewGoal(userDTO))
			return null;

		Goal goalEntity = EntityFactory.createGoal();
		String title = goalDTO.getTitle();
		title = com.igoal.utils.StringUtils.removeSpacesInsideString(title);
		title = com.igoal.utils.StringUtils.removeAllHTMLTags(title);
		
		if(StringUtils.isBlank(title)){
			error("New goal title is empty.");
			return null;
		}	
		
		if (title.length() > 255) {
			error("New goal title length is greater than 255");
			return null;
		}

		goalDTO.setTitle(title);
		goalDTO.copyAttributesTo(goalEntity);

		try {
			debug("Finding user has id[" + userDTO.getId() + "]");
			User userEntity = userDAO.get(userDTO.getId());
			Assert.notNull(userEntity);

			debug("User[" + userEntity.getUsername() + "] request add a goal: "
					+ goalDTO.getTitle());

			if (!goalDAO.exists(goalEntity.getTitle())) {
				debug("The requested goal[" + goalDTO.getTitle()
						+ "] is not exist. Create new into database...");
				goalEntity.setStatus(DataStatus.USING);
				goalEntity.setCreateDate(new Date());
				goalEntity.setUpdateDate(new Date());
				goalEntity = goalDAO.save(goalEntity);
			} else {
				debug("The request goal[" + goalDTO.getTitle() + "] is exist.");
				goalEntity = goalDAO.findByTitle(goalDTO.getTitle());
				Assert.notNull(goalEntity);
			}

			if (!userGoalDAO.exists(userEntity.getId(), goalEntity.getId())) {
				UserGoal userGoal = EntityFactory.createUserGoal(userEntity,
						goalEntity);

				// set privacy
				PrivacyEnum privacyLevel = PrivacyEnum.PUBLIC;
				PrivacySetting privacy = privacySettingDAO.getPrivacy(privacyLevel);
				userGoal.setPrivacy(privacy);
				
				EntityUtils.increasePropertyValue(goalEntity, "userCount");

				userGoal = userGoalDAO.save(userGoal);
				goalEntity = goalDAO.save(goalEntity);
				EntityUtils.increasePropertyValue(userEntity, "numberGoals");
				userDAO.save(userEntity);

				int result = userGoal != null ? SaveResult.SUCCESS
						: SaveResult.FAIL;
				if (result == SaveResult.SUCCESS) {
					debug("Added goal[" + goalDTO.getTitle() + "] to user["
							+ userDTO.getUsername() + "] successfully.");
					UserGoalDTO dto = new UserGoalDTO();
					dto.initFrom(userGoal);

					// update profile complete for this user
					ProfileComplete profileComplete = profileCompleteDAO
							.findByPK(userDTO.getId());
					if (profileComplete == null) {
						profileComplete = EntityFactory
								.createProfileComplete(userEntity);
					}
					if (!profileComplete.getAddGoal()) {
						profileComplete.setAddGoal(true);
						profileCompleteDAO.save(profileComplete);
					}
					
					updateEntity(userEntity);
					userDAO.save(userEntity);
					
					actionLoggerService.log(userEntity, userEntity,
							ActionTypeEnum.NEW_GOAL, ComponentEnum.GOAL,
							goalEntity.getId(), privacy);								
					
					return dto;
				} else {
					debug("Error occured when try adding goal["
							+ goalDTO.getTitle() + "] to user["
							+ userDTO.getUsername() + "]");
					return null;
				}
			} else {
				debug("User[" + userDTO.getUsername() + "] has added this goal["
						+ goalDTO.getTitle() + "] already.");
				return null;
			}
		} catch (Exception e) {
			logger.trace("Fail to create new user goal", e);
			return null;
		}
	}
	
	public boolean allowCreateNewGoal(UserDTO user) {
		int createdGoalInToday = userGoalDAO.countCreatedUserGoalInToday(user.getId());
		return createdGoalInToday < Constants.MAX_GOAL_CAN_CREATE_PER_DAY;
	}
	
	public boolean isThisGoalInUserGoalList(String username, String goalTitle) {
		if (StringUtils.isBlank(username) || StringUtils.isBlank(goalTitle)) {
			return false;
		}

		try {
			User user = (User) userDAO.loadUserByUsername(username);
			Goal goal = goalDAO.findByTitle(goalTitle);

			if (goal == null)
				return false;

			return userGoalDAO.exists(user.getId(), goal.getId());
		} catch (Exception e) {
			logger.error(e);
			return false;
		}
	}

	public UserGoalDTO getUserGoalByPrimaryKey(Long userId, Long goalId) {
		try {
			UserGoal userGoal = userGoalDAO.findByPK(userId, goalId);
			if (userGoal == null)
				return null;

			UserGoalDTO dto = new UserGoalDTO();
			dto.initFrom(userGoal);
			return dto;
		} catch (Exception e) {
			return null;
		}
	}
	
	public int deleteUserGoal(String username, Long goalId) {
		try {
			User user = (User) userDAO.loadUserByUsername(username);
			UserGoal userGoal = userGoalDAO.findByPK(user.getId(), goalId);
			if (userGoal != null) {
				userGoal.setStatus(DataStatus.DELETED);
				userGoal.setUpdateDate(new Date());
				userGoalDAO.save(userGoal);

				List<UserGoalEntry> goalEntries = userGoalEntryDAO
						.findByFK(userGoal.getPk());
				for (UserGoalEntry entry : goalEntries) {
					entry.setStatus(DataStatus.DELETED);
					entry.setUpdateDate(new Date());
					userGoalEntryDAO.save(entry);
				}

				List<UserGoalDone> doneEntries = userGoalEntryDoneDAO
						.findByFK(userGoal.getPk());
				for (UserGoalDone doneEntry : doneEntries) {
					doneEntry.setStatus(DataStatus.DELETED);
					doneEntry.setUpdateDate(new Date());
					userGoalEntryDoneDAO.save(doneEntry);
				}
				
				// update total number users of this goal
				Goal goal = userGoal.getGoal();
				if (userGoal.getGoalStatus().equals(GoalStatusEnum.DOING)) {
//					goal.setUserCount(goal.getUserCount() - 1);
					EntityUtils.descreasePropertyValue(goal, "userCount");
				} else if(userGoal.getGoalStatus().equals(GoalStatusEnum.FINISHED)){
//					goal.setDoneCount(goal.getDoneCount() - 1);
					EntityUtils.descreasePropertyValue(goal, "doneCount");
				}
				goalDAO.save(goal);

				// update total goal of user
//				user.setNumberGoals(user.getNumberGoals() - 1);
				EntityUtils.descreasePropertyValue(user, "numberGoals");
				userDAO.save(user);

				return SaveResult.SUCCESS;
			}
			return SaveResult.FAIL;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}
	
	public int achieveGoal(String username, Long goalId) {
		debug(username + " achieved the goal " + goalId);

		User user = null;

		try {
			user = (User) userDAO.loadUserByUsername(username);
		} catch (UsernameNotFoundException e) {
			return SaveResult.FAIL;
		}

		UserGoal userGoal = userGoalDAO.findByPK(user.getId(), goalId);
		// TODO review rule - not do this goal but finish it
		if (userGoal == null) {
			return SaveResult.FAIL;
		}

		try {
			userGoal.setGoalStatus(GoalStatusEnum.FINISHED);
			userGoal.setPercent(100);
			userGoalDAO.save(userGoal);

			// update number of people who has achieved this goal
			Goal goal = userGoal.getGoal();
			// goal.setDoneCount(goal.getDoneCount() + 1);
			EntityUtils.increasePropertyValue(goal, "doneCount");
			// goal.setUserCount(goal.getUserCount() - 1);
			EntityUtils.descreasePropertyValue(goal, "userCount");
			goalDAO.save(goal);

			updateEntity(user);
			userDAO.save(user);

			actionLoggerService.log(user, user, ActionTypeEnum.FINISH_GOAL,
					ComponentEnum.GOAL, goalId, userGoal.getPrivacy());
		} catch (Exception e) {
			error("Cannot update goal status", e);
			return SaveResult.FAIL;
		}

		return SaveResult.SUCCESS;
	}

	public int doGoal(String username, Long goalId) {
		User user = null;
		try {
			user = (User) userDAO.loadUserByUsername(username);
		} catch (UsernameNotFoundException e) {
			return SaveResult.FAIL;
		}

		Goal goal = goalDAO.get(goalId);
		if (goal == null) {
			debug("Goal with id " + goalId + " does not exist");
			return SaveResult.FAIL;
		}

		UserGoal userGoal = userGoalDAO.findByPK(user.getId(), goalId);
		if (userGoal != null) {
			if (!userGoal.getStatus().equals(DataStatus.DELETED)) {
				debug("Already did that goal");
				return SaveResult.FAIL;
			} else {
				userGoal.setStatus(DataStatus.USING);
			}
		} else {
			userGoal = EntityFactory.createUserGoal(user, goal);
		}

		// set privacy
		PrivacyEnum privacyLevel = PrivacyEnum.PUBLIC;
		PrivacySetting privacy = privacySettingDAO.getPrivacy(privacyLevel);
		userGoal.setPrivacy(privacy);
		
		userGoal = userGoalDAO.save(userGoal);
		
		// increase number of doing users
		EntityUtils.increasePropertyValue(goal, "userCount");
		goal = goalDAO.save(goal);
		EntityUtils.increasePropertyValue(user, "numberGoals");
		userDAO.save(user);

		int result = userGoal != null ? SaveResult.SUCCESS : SaveResult.FAIL;
		if (result == SaveResult.SUCCESS) {
			debug("User[" + user.getUsername() + "] did the goal ["
					+ goal.getTitle() + "]");
			actionLoggerService.log(user, user, ActionTypeEnum.NEW_GOAL,
					ComponentEnum.GOAL, goalId, privacy);
		} else {
			debug("Error occured when try adding goal[" + goal.getTitle()
					+ "] to user[" + user.getUsername() + "]");
		}

		return result;
	}
	
	public int updateUserGoalPercent(String username, Long goalId,
			Integer percent) {
		if (StringUtils.isBlank(username) || goalId == null || percent == null
				|| percent < 0)
			return SaveResult.FAIL;

		try {
			User user = (User) userDAO.loadUserByUsername(username);

			UserGoal userGoal = userGoalDAO.findByPK(user.getId(), goalId);
			if (userGoal == null)
				return SaveResult.FAIL;

			userGoal.setPercent(percent);
			userGoalDAO.save(userGoal);
			
			updateEntity(user);
			userDAO.save(user);
			
			actionLoggerService.log(user, user, ActionTypeEnum.UPDATE_GOAL, ComponentEnum.GOAL, goalId);
			
			return SaveResult.SUCCESS;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}
	
	public List<UserGoalDTO> getUserGoalsByPaging(Map<String, Object> criteria,
			String orderProperty, Object lastOrderPropertyValue, boolean before) {
		List<UserGoalDTO> result = new ArrayList<UserGoalDTO>();

		List<UserGoal> userGoals = userGoalDAO.pagingDependOnOrderProperty(
				criteria, "updateDate", lastOrderPropertyValue, before,
				PagingService.MAX_GOALS_OF_USER);
		if (userGoals != null) {
			for (UserGoal userGoal : userGoals) {
				UserGoalDTO dto = new UserGoalDTO();
				int entryCount = countGoalEntry(userGoal.getGoal().getId(),
						userGoal.getUser().getId());
				dto.setEntryCount(entryCount);
				dto.initFrom(userGoal);

				if (isLoginUser()
						&& getUserInSession().equals(
								userGoal.getUser().getUsername())) {
					dto.setShowActionControl("yes");
				} else {
					dto.setShowActionControl("no");
				}

				result.add(dto);
			}
		}
		return result;
	}

	public boolean exists(UserGoalPk id) {
		return userGoalDAO.exists(id);
	}

	public UserGoal get(UserGoalPk id) {				
		return userGoalDAO.get(id);
	}

	public List<UserGoal> getAll() {
		return userGoalDAO.getAll();
	}

	public void remove(UserGoalPk id) {
		userGoalDAO.remove(id);
	}

	public UserGoal save(UserGoal object) {
		return userGoalDAO.save(object);
	}

	public List<UserGoalTaskDTO> getTodoList(String username, Long goalId) {
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			List<UserGoalTask> tasks = userGoalTaskDAO.findByUserId(user.getId());
			List<UserGoalTaskDTO> result = new ArrayList<UserGoalTaskDTO>();
			for(UserGoalTask task : tasks){
				UserGoalTaskDTO dto = new UserGoalTaskDTO();
				dto.initFrom(task);
				result.add(dto);
			}
			
			return result;
		}catch (Exception e) {
			logger.error(e);
		}
		
		return null;
	}

	public Long addPhaseToUserGoal(UserGoalPhaseDTO phase){
		if(phase == null)
			return new Long(SaveResult.FAIL);
		
		User user = userDAO.get(phase.getUserId());
		Assert.notNull(user);
		Goal goal = goalDAO.get(phase.getGoalId());
		Assert.notNull(goal);
		
		if(!userGoalPhaseDAO.exists(phase.getUserId(), phase.getGoalId(), phase.getPhaseName())){
			UserGoal userGoal = userGoalDAO.findByPK(phase.getUserId(), phase.getGoalId());
			UserGoalPhase entity = EntityFactory.createUserGoalPhase(userGoal);
			phase.copyAttributesTo(entity);
			
			entity = userGoalPhaseDAO.save(entity);
			
			UserGoalTask task = EntityFactory.createUserGoalTask(entity);
			userGoalTaskDAO.save(task);
			
			return entity.getId();
		}else{
			logger.error("User["+user.getUsername()+"] request add phase that exists");
		}
		
		return new Long(SaveResult.FAIL);
	}

	public int updateTask(String username, UserGoalTaskDTO task) {
		if(StringUtils.isBlank(username) || task == null)
			return SaveResult.FAIL;
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			UserGoalTask entity = userGoalTaskDAO.get(task.getId());
			if(user.equals(entity.getUser())){
				task.copyAttributesTo(entity);
				userGoalTaskDAO.save(entity);
				
				return SaveResult.SUCCESS;
			}else{
				logger.error("User["+user.getUsername()+"] requested update a task of other people.");
			}
		}catch (Exception e) {
			logger.error(e);
		}
		
		return SaveResult.FAIL;
	}
	
	public int updatePhases(String username, List<UserGoalPhaseDTO> phases){
		if(StringUtils.isBlank(username) || phases == null || phases.size() == 0)
			return SaveResult.FAIL;
		
		int result = 0;
		for(UserGoalPhaseDTO phase : phases){
			result += updatePhase(username, phase);
		}
		
		if(result == phases.size()){
			return SaveResult.SUCCESS;
		}
		
		return SaveResult.FAIL;
	}
	
	public int updatePhase(String username, UserGoalPhaseDTO phase){
		if(StringUtils.isBlank(username) || phase == null)
			return SaveResult.FAIL;
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			UserGoalPhase entity = userGoalPhaseDAO.get(phase.getId());
			if(user.equals(entity.getUser())){
				phase.copyAttributesTo(entity);
				updateEntity(entity);
				userGoalPhaseDAO.save(entity);
				
				//TODO: rework when support multiple tasks
				UserGoalTask[] tasks = new UserGoalTask[entity.getTaskList().size()];
				entity.getTaskList().toArray(tasks);
				UserGoalTask task = tasks[0];
				task.setFinished(entity.getFinished());
				updateEntity(task);
				userGoalTaskDAO.save(task);
				
				scanPhaseTasks(entity);
				return SaveResult.SUCCESS;
			}else{
				logger.error("User["+user.getUsername()+"] requested update a phase of other people.");
			}
		}catch (Exception e) {
			logger.error(e);
		}
		
		return SaveResult.FAIL;
	}
	
	public void scanPhaseTasks(UserGoalPhase phase){
		if(phase == null)
			return;
		
		Set<UserGoalTask> tasks = phase.getTaskList();
		boolean allFinished = true; 
		for(UserGoalTask task : tasks){
			if(!task.getFinished().booleanValue()){
				allFinished = false;
				break;
			}
		}
		
		if(allFinished){
			phase.setFinished(Boolean.TRUE);
			phase.setFailed(Boolean.FALSE);
		}else{
			phase.setFinished(Boolean.FALSE);
		}
		
		userGoalPhaseDAO.save(phase);
		
		scanUserGoalPhase(phase.getUserGoal());
	}
	
	public void scanUserGoalPhase(UserGoal userGoal){
		if(userGoal == null)
			return;
		
		List<UserGoalPhase> phases = userGoal.getPhaseList();
		int totalFinishedPercent = 0;
		
		for(UserGoalPhase phase : phases){
			if(phase.getFinished().booleanValue()){
				totalFinishedPercent += phase.getPercentMeasure().intValue();
			}
		}
		
		userGoal.setPercent(new Integer(totalFinishedPercent));
		if(totalFinishedPercent == 100){
			userGoal.setGoalStatus(GoalStatusEnum.FINISHED);
		}else{
			userGoal.setGoalStatus(GoalStatusEnum.DOING);
		}
		userGoalDAO.save(userGoal);
	}

	public int finishTask(String username, Long taskId) {
		if(StringUtils.isBlank(username) || taskId == null || taskId <= 0)
			return SaveResult.FAIL;
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			UserGoalTask entity = userGoalTaskDAO.get(taskId);
			if(user.equals(entity.getUser())){
				if(!entity.getFinished().booleanValue()){
					entity.setFinished(Boolean.TRUE);
					userGoalTaskDAO.save(entity);
					
					scanPhaseTasks(entity.getUserGoalPhase());
				}
				
				return SaveResult.SUCCESS;
			}else{
				logger.error("User["+user.getUsername()+"] requested update a task of other people.");
			}
		}catch (Exception e) {
			logger.error(e);
		}
		return SaveResult.FAIL;
	}
	
	public int uncompleteTask(String username, Long taskId) {
		if(StringUtils.isBlank(username) || taskId == null || taskId <= 0)
			return SaveResult.FAIL;
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			UserGoalTask entity = userGoalTaskDAO.get(taskId);
			if(user.equals(entity.getUser())){
				entity.setFinished(false);
				userGoalTaskDAO.save(entity);
				
				scanPhaseTasks(entity.getUserGoalPhase());
				
				return SaveResult.SUCCESS;
			}else{
				logger.error("User["+user.getUsername()+"] requested update a task of other people.");
			}
		}catch (Exception e) {
			logger.error(e);
		}
		return SaveResult.FAIL;
	}

	public int deleteTask(String username, Long taskId) {
		if(StringUtils.isBlank(username) || taskId == null || taskId <= 0)
			return SaveResult.FAIL;
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			UserGoalTask entity = userGoalTaskDAO.get(taskId);
			UserGoalPhase phase = entity.getUserGoalPhase();
			if(user.equals(entity.getUser())){
				userGoalTaskDAO.remove(taskId);
				
				scanPhaseTasks(phase);
				
				return SaveResult.SUCCESS;
			}else{
				logger.error("User["+user.getUsername()+"] requested update a task of other people.");
			}
		}catch (Exception e) {
			logger.error(e);
		}
		return SaveResult.FAIL;
	}

	public List<UserGoalTaskDTO> getUnCompleteTasks(String username) {
		if(StringUtils.isBlank(username))
			return null;
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			List<UserGoalTask> tasks = userGoalTaskDAO.getUnCompleteTask(user.getId(), null);
			List<UserGoalTaskDTO> result = new ArrayList<UserGoalTaskDTO>();
			
			for(UserGoalTask task : tasks){
				UserGoalTaskDTO dto = new UserGoalTaskDTO();
				dto.initFrom(task);
				result.add(dto);
			}
			
			return result;
		}catch (Exception e) {
			logger.error(e);
		}
		
		return null;
	}

	public List<UserGoalTaskDTO> getUnCompleteTasks(String username, Long goalId) {
		if(StringUtils.isBlank(username))
			return null;
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			
			Goal goal = goalDAO.get(goalId);
			if(goal == null){
				logger.error("Cannot find goal by id:" + goalId);
				return null;
			}
			
			List<UserGoalTask> tasks = userGoalTaskDAO.getUnCompleteTask(user.getId(), goal.getId());
			List<UserGoalTaskDTO> result = new ArrayList<UserGoalTaskDTO>();
			
			for(UserGoalTask task : tasks){
				UserGoalTaskDTO dto = new UserGoalTaskDTO();
				dto.initFrom(task);
				result.add(dto);
			}
			
			return result;
		}catch (Exception e) {
			logger.error(e);
		}
		
		return null;
	}

	public List<UserGoalTaskDTO> notifyNearDeadLineTasks(String username) {
		if(StringUtils.isBlank(username))
			return null;
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			Date beginPeriod = Calendar.getInstance().getTime();
			beginPeriod = CalendarUtils.getStartDate(beginPeriod);
			
			Calendar.getInstance().add(Calendar.DAY_OF_MONTH, Constants.DAY_WILL_REMIND_USER_ABOUT_TASK_DEADLINE);
			Date endPeriod = Calendar.getInstance().getTime();
			
			List<UserGoalTask> tasks = userGoalTaskDAO.getTasksInPeriodTime(user.getId(), null, beginPeriod, endPeriod, false);
			List<UserGoalTaskDTO> result = new ArrayList<UserGoalTaskDTO>();
			
			for(UserGoalTask task : tasks){
				UserGoalTaskDTO dto = new UserGoalTaskDTO();
				dto.initFrom(task);
				result.add(dto);
			}
			
			return result;
		}catch (Exception e) {
			logger.error(e);
		}
		return null;
	}
	
	public int addOrUpdateSMARTGoal(String username, UserGoalDTO userGoal, List<UserGoalPhaseDTO> phases){
		if(userGoal == null || phases == null || phases.size() == 0){//TODO: check phases or not?
			return SaveResult.FAIL;
		}
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			Long userId = user.getId();
			Long goalId = userGoal.getGoalId();
			
			UserDTO userDTO = new UserDTO();
			userDTO.setId(user.getId());
			
			GoalDTO goalDTO = new GoalDTO();
			goalDTO.setTitle(userGoal.getTitle());
			
			Integer taskCount = phases != null ? phases.size() : 0;		
			String value = userGoal.getValue();
			String deadLine = userGoal.getDeadLine();
			int userGoalPercent = 0;
			if(userGoalDAO.exists(userId, goalId)){
				//update
				UserGoal userGoalEntity = userGoalDAO.findByPK(userId, goalId);
				userGoal.copyAttributesTo(userGoalEntity);				
				userGoalEntity.setTaskCount(taskCount);
				userGoalEntity.setValue(value);
				userGoalEntity.setPercent(0);												
				Date deadLineDate = CalendarUtils.parseDate(deadLine, "dd/MM/yyyy");
				userGoalEntity.setDeadLine(deadLineDate);
				userGoalEntity.setStatus(DataStatus.USING);
				updateEntity(userGoalEntity);
				
				userGoalDAO.save(userGoalEntity);
				
				for(UserGoalPhaseDTO phase : phases){
					if(phase.getId() != null){
						//update
						UserGoalPhase phaseEntity = userGoalPhaseDAO.get(phase.getId());
						phase.copyAttributesTo(phaseEntity);
						updateEntity(phaseEntity);
						phaseEntity = userGoalPhaseDAO.save(phaseEntity);
						
						//TODO: rework if allow multiple tasks inside one phase
						UserGoalTask[] tasks = new UserGoalTask[phaseEntity.getTaskList().size()]; 
						phaseEntity.getTaskList().toArray(tasks);
						
						UserGoalTask task = tasks[0];
						task.setUserGoalPhase(phaseEntity);				
						task.setPercentMeasure(phaseEntity.getPercentMeasure());
						task.setDeadLine(phaseEntity.getDeadLine());
						task.setTaskName(phaseEntity.getPhaseName());
						task.setFinished(phaseEntity.getFinished());
						updateEntity(task);
						userGoalTaskDAO.save(task);
						
						if(Boolean.TRUE.equals(phaseEntity.getFinished())){
							userGoalPercent += phaseEntity.getPercentMeasure().intValue();
						}
					}else{
						phase.setUserId(user.getId());
						phase.setGoalId(userGoal.getGoalId());
						addPhaseToUserGoal(phase);
					}
				}
				
				scanUserGoalPhase(userGoalEntity);
				return SaveResult.SUCCESS;
			}else{
				return addSMARTGoal(username, userGoal, phases);
			}
		}catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
		
	}
	
	public int addSMARTGoal(String username, UserGoalDTO userGoal, List<UserGoalPhaseDTO> phases){
		if(userGoal == null || phases == null || phases.size() == 0){//TODO: check phases or not?
			return SaveResult.FAIL;
		}
		
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			UserDTO userDTO = new UserDTO();
			userDTO.setId(user.getId());
			
			GoalDTO goalDTO = new GoalDTO();
			goalDTO.setTitle(userGoal.getTitle());
			
			Integer taskCount = phases != null ? phases.size() : 0;		
			String value = userGoal.getValue();
			String deadLine = userGoal.getDeadLine();
			
			userGoal = createNewGoal(goalDTO, userDTO);
			
			if(userGoal == null)
				throw new Exception("Cannot create new goal["+goalDTO.getTitle()+"] for user[id="+userDTO.getId()+"]");
			
			if(phases != null && !phases.isEmpty()){
				for(UserGoalPhaseDTO phase : phases){
					phase.setUserId(userGoal.getUserId());
					phase.setGoalId(userGoal.getGoalId());
					addPhaseToUserGoal(phase);
				}				
				UserGoal goalOfUser = userGoalDAO.findByPK(userGoal.getUserId(), userGoal.getGoalId());
				goalOfUser.setTaskCount(taskCount);
				goalOfUser.setValue(value);
								
				Date deadLineDate = CalendarUtils.parseDate(deadLine, "dd/MM/yyyy");
				goalOfUser.setDeadLine(deadLineDate);
			
				userGoalDAO.save(goalOfUser);
			}
			
			return SaveResult.SUCCESS;
		}catch (Exception e) {
			logger.error(e);
		}
		
		return SaveResult.FAIL;
	}
	
	public List<UserGoalPhaseDTO> getPhaseList(String username, Long goalId) {
		try{
			User user = (User) userDAO.loadUserByUsername(username);
			List<UserGoalPhase> phases = userGoalPhaseDAO.findByUserGoalId(user.getId(), goalId);
			List<UserGoalPhaseDTO> result = new ArrayList<UserGoalPhaseDTO>();
			for(UserGoalPhase phase : phases){
				UserGoalPhaseDTO phaseDTO = new UserGoalPhaseDTO();
				phaseDTO.initFrom(phase);
				result.add(phaseDTO);
			}			
			
			return result;
		}catch (Exception e) {
			logger.error(e);
		}
		
		return null;
	}
}
